import pygame, sys, os
from pygame.locals import *
import pygame, sys, os
from pygame.locals import *
import speech_recognition as sr
from aip import AipSpeech
from collections import deque
import speech_recognition as sr
from collections import deque
import tkinter as tk
import tkinter
import tkinter.messagebox#弹窗库
APP_ID = '31958523'
API_KEY = 'RS2GM39mUASP9BD5kaMMzyW8'
SECRET_KEY = 'nD1T98lzG2RVxgPxwBaUGTDIXjF0voGS'
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
path = 'voices/voice.wav'  ##需要提前创建voices文件夹
LEVEL_KEY=0#控制关卡

# 使用speech_recognition录音
def record_audio(rate=16000):
    r = sr.Recognizer()
    with sr.Microphone(sample_rate=rate) as source:
        print("please say something")
        r.adjust_for_ambient_noise(source)  # 动态调整能量阈值以解决环境噪声（自动调整静音检测的阈值）（使用后可显著提高效果）
        audio = r.listen(source, phrase_time_limit=2)  # 限制录音的最长时长为59秒，防止超出百度的时间限制
        print('record finish')

    with open("voices/voice.wav", "wb") as f:
        f.write(audio.get_wav_data())


# 调用百度语音转文字接口，将语音转文本
def listen():
    with open(path, 'rb') as fp:
        voice = fp.read()

    result = client.asr(voice, 'wav', 16000, {'dev_pid': 1537})
    try:
        result_text = result["result"][0]
        print(result_text)
        sentence = str(result_text)
        comlist = ['上', '下', '左', '右']
        senlist = []
        for i in sentence:
            senlist.append(i)
        for command in senlist:
            p = senlist.index(command)
            if command == "撤" and senlist[p + 1] == '销':
                print("撤销")
                return ("撤销")
            elif command in comlist:
                if senlist[p+1]=="跳":
                    print("向"+command+"跳")
                    return "向"+command+"跳"
                else:
                    print(command)
                    return (command)
    except KeyError:
        print("KeyError")
        print(result)



def to_box(level, index):
    if level[index] == '-' or level[index] == '@':#判断箱子移动之后的位置，空白或者是人（为了撤销）
        level[index] = '$'
    else:
        level[index] = '*'

def to_man(level, i):
    if level[i] == '-' or level[i] == '$':#如果人的下一步是空白或者箱子
        level[i]='@'
    else:
        level[i]='+'

def to_floor(level, i):
    if level[i] == '@' or level[i] == '$':
        level[i]='-'
    else:
        level[i]='.'

def to_offset(d, width):
    d4 = [-1, -width, 1, width,-2, -2*width, 2, 2*width]
    m4 = ['l','u','r','d','a','w','f','s']
    return d4[m4.index(d.lower())]
def jump_man(level,i):
    if level[i] == '-':
        level[i] = '@'
    else:
        level[i] = '+'

def jump_floor(level,i):
    if level[i] == '@' or level[i] == '$':
        level[i]='-'
    else:
        level[i] = '.'

def jump_offset(d,width):
    d5=[-2, -2*width, 2, 2*width]
    m5=['a','w','f','s']
    return d5[m5.index(d.lower())]


class Sokoban:
    def __init__(self):
        self.level = list(
            "----#####----------"
            "----#---#----------"
            "----#---#----------"
            "--###---##---------"
            "--#------#---------"
            "###------#---######"
            "#--------#####----#"
            "#-----------$.----#"
            "#####-###-#@##----#"
            "----#-----#########"
            "----#######--------")
        self.count = 1
        self.w = 19
        self.h = 11
        self.man = 163
        self.hint = list(self.level)
        self.solution = []
        self.push = 0
        self.todo = []
        self.auto = 0
        self.sbox = 0
        self.queue = []
    def draw(self, screen, skin):
        w = skin.get_width() / 4
        offset = (w-4)/2
        for i in range(0,self.w):
            for j in range(0,self.h):
                if self.level[j*self.w + i] == '#':
                    screen.blit(skin, (i*w, j*w), (0,2*w,w,w))
                elif self.level[j*self.w + i] == '-':
                    screen.blit(skin, (i*w, j*w), (0,0,w,w))
                elif self.level[j*self.w + i] == '@':
                    screen.blit(skin, (i*w, j*w), (w,0,w,w))
                elif self.level[j*self.w + i] == '$':
                    screen.blit(skin, (i*w, j*w), (2*w,0,w,w))
                elif self.level[j*self.w + i] == '.':
                    screen.blit(skin, (i*w, j*w), (0,w,w,w))
                elif self.level[j*self.w + i] == '+':
                    screen.blit(skin, (i*w, j*w), (w,w,w,w))
                elif self.level[j*self.w + i] == '*':
                    screen.blit(skin, (i*w, j*w), (2*w,w,w,w))
                if self.sbox != 0 and self.hint[j*self.w+i]=='1':
                    screen.blit(skin,  (i*w+offset, j*w+offset), (3*w, 3*w, 4, 4))
    def move(self, d):
        self._move(d)
        self.todo = []
    def _move(self, d):
        self.sbox = 0
        h = to_offset(d, self.w)#人的位移
        h2 = 2 * h#如果推着箱子，箱子在人的下一步
        if self.level[self.man + h] == '-' or self.level[self.man + h] == '.':#判断下一步是空白或终点（人可以正常走）
        # move
            to_man(self.level, self.man+h)
            to_floor(self.level, self.man)
            self.man += h
            self.solution += d
        elif self.level[self.man + h] == '*' or self.level[self.man + h] == '$':#人的下一步是箱子或是箱子与终点重合
            if self.level[self.man + h2] == '-' or self.level[self.man + h2] == '.':#箱子的操作，因为已经判断为箱子
            # push
                to_box(self.level, self.man + h2)
                to_man(self.level, self.man + h)
                to_floor(self.level, self.man)
                self.man += h
                self.solution += d.upper()
                self.push += 1

    def jump(self, d):
        h = jump_offset(d, self.w)  # 人的位移
        if self.level[self.man + h] == '-' or self.level[self.man + h] == '.':  # 判断下一步是空白或终点（人可以正常走）
            to_man(self.level, self.man + h)
            to_floor(self.level, self.man)
            self.man += h
            self.solution += d
    def undo(self):
        if self.solution.__len__()>0:
            self.todo.append(self.solution[-1])
            self.solution.pop()

            h = to_offset(self.todo[-1],self.w) * -1
            if self.todo[-1].islower():
            #undo a move
                to_man(self.level, self.man + h)
                to_floor(self.level, self.man)
                self.man += h
            else:
            # undo a push
                to_floor(self.level, self.man - h)
                to_box(self.level, self.man)
                to_man(self.level, self.man + h)
                self.man += h
                self.push -= 1
    def redo(self):
        if self.todo.__len__()>0:
            self._move(self.todo[-1].lower())
            self.todo.pop()

    def automove(self):
        if self.auto == 1 and self.todo.__len__()>0:
            self._move(self.todo[-1].lower())
            self.todo.pop()
        else:
            self.auto = 0



def main():

    # start pygame
    pygame.init()
    screen = pygame.display.set_mode((400,300))


    # load skin
    skinfilename = os.path.join('borgar.png')
    try:
        skin = pygame.image.load(skinfilename)
    except pygame.error as msg:
        print('cannot load skin')
        raise SystemExit(msg)
    skin = skin.convert()

    #print(skin.get_at((0,0))
    #screen.fill((255,255,255))
    screen.fill(skin.get_at((0,0)))#先将游戏界面初始化，调成图片的（0，0）位置图案

    pygame.display.set_caption('sokoban.py')

    # create Sokoban object
    skb = Sokoban()#初始化对象
    skb.draw(screen,skin)#画出游戏图案，图片的每一块对应

    #
    clock = pygame.time.Clock()
    pygame.key.set_repeat(200,50)#获取重复的键盘事件

    pygame.display.update()
    pygame.display.set_caption(skb.solution.__len__().__str__() + '/' + skb.push.__str__() + ' - sokoban.py')
    # main game loop
    while True:
        clock.tick(60)
        record_audio()
        a = listen()
        if a == '左':
            skb.move('l')
            skb.draw(screen, skin)
        elif a == '上':
            skb.move('u')
            skb.draw(screen, skin)
        elif a == '右':
            skb.move('r')
            skb.draw(screen, skin)
        elif a == '下':
            skb.move('d')
            skb.draw(screen, skin)
        elif a == '撤销':
            skb.undo()
            skb.draw(screen, skin)
        elif a == '向上跳':
            skb.jump('w')
            skb.draw(screen, skin)
        elif a == '向下跳':
            skb.jump('s')
            skb.draw(screen, skin)
        elif a == '向左跳':
            skb.jump('a')
            skb.draw(screen, skin)
        elif a == '向右跳':
            skb.jump('f')
            skb.draw(screen, skin)
        if skb.level.count('*') == skb.count:
            global LEVEL_KEY

            root = tkinter.Tk()
            root.withdraw()
            if LEVEL_KEY == 0:
                tkinter.messagebox.showinfo('恭喜！', '通关成功，点击确定进入下一关！')
                skb = Sokoban()  #
                skb.level = list(
                    "----#####----------"
                    "----#---#----------"
                    "----#---#----------"
                    "--###---##---------"
                    "--#------#---------"
                    "###------#---######"
                    "#--------#####----#"
                    "#--------.$-------#"
                    "#####-###-#@##----#"
                    "----#-----#########"
                    "----#######--------")
                skb.draw(screen, skin)

            if LEVEL_KEY == 1:
                root = tkinter.Tk()
                root.withdraw()
                tkinter.messagebox.showinfo('恭喜！', '通关成功')
                sys.exit()
            LEVEL_KEY = LEVEL_KEY + 1

        for event in pygame.event.get():
            if event.type == QUIT:
                # print skb.solution
                pygame.quit()
                sys.exit()




        pygame.display.update()
        pygame.display.set_caption(skb.solution.__len__().__str__() + '/' + skb.push.__str__() + ' - sokoban.py')


if __name__ == '__main__':
    main()
